


#include "qe_math.h"
#include "qe_string.h"
#include "qe_memory.h"
#include "qe_macros.h"
#include "qe_assert.h"
#include <stdarg.h>



#define _ISDIGIT(c)  ((unsigned)((c) - '0') < 10)

#if defined(CONFIG_NANO)
/**
 * strncpy - Copy a length-limited, %NUL-terminated string
 * @dest: Where to copy the string to
 * @src: Where to copy the string from
 * @count: The maximum number of bytes to copy
 *
 * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
 * However, the result is not %NUL-terminated if the source exceeds
 * @count bytes.
 */
char *qe_strncpy(char *dest, const char *src, qe_size count)
{
	char *tmp = dest;

	while (count-- && (*dest++ = *src++) != '\0')
		/* nothing */;

	return tmp;
}

qe_size qe_strlcpy(char *dest, const char *src, qe_size size)
{
	qe_size ret = qe_strlen(src);

	if (size) {
		qe_size len = (ret >= size) ? size - 1 : ret;
		qe_memcpy(dest, (void *)src, len);
		dest[len] = '\0';
	}
	return ret;
}

/**
 * strcat - Append one %NUL-terminated string to another
 * @dest: The string to be appended to
 * @src: The string to append to it
 */
char *qe_strcat(char *dest, const char *src)
{
	char *tmp = dest;

	while (*dest)
		dest++;
	while ((*dest++ = *src++) != '\0')
		;

	return tmp;
}

/**
 * strncat - Append a length-limited, %NUL-terminated string to another
 * @dest: The string to be appended to
 * @src: The string to append to it
 * @count: The maximum numbers of bytes to copy
 *
 * Note that in contrast to strncpy, strncat ensures the result is
 * terminated.
 */
char *qe_strncat(char *dest, const char *src, qe_size count)
{
	char *tmp = dest;

	if (count) {
		while (*dest)
			dest++;
		while ((*dest++ = *src++)) {
			if (--count == 0) {
				*dest = '\0';
				break;
			}
		}
	}

	return tmp;
}

/**
 * strcmp - Compare two strings
 * @cs: One string
 * @ct: Another string
 */
int qe_strcmp(const char *cs, const char *ct)
{
	register signed char __res;

	while (1) {
		if ((__res = *cs - *ct++) != 0 || !*cs++)
			break;
	}

	return __res;
}

/**
 * strncmp - Compare two length-limited strings
 * @cs: One string
 * @ct: Another string
 * @count: The maximum number of bytes to compare
 */
int qe_strncmp(const char *cs, const char *ct, qe_size count)
{
	register signed char __res = 0;

	while (count) {
		if ((__res = *cs - *ct++) != 0 || !*cs++)
			break;
		count--;
	}

	return __res;
}

/**
 * strchr - Find the first occurrence of a character in a string
 * @s: The string to be searched
 * @c: The character to search for
 */
char *qe_strchr(const char *s, int c)
{
	for(; *s != (char) c; ++s)
		if (*s == '\0')
			return QE_NULL;
	return (char *) s;
}

const char *qe_strchrnul(const char *s, int c)
{
	for (; *s != (char)c; ++s)
		if (*s == '\0')
			break;
	return s;
}

/**
 * strcpy - Copy a %NUL terminated string
 * @dest: Where to copy the string to
 * @src: Where to copy the string from
 */
char *qe_strcpy(char *dest, const char *src)
{
	char *tmp = dest;

	while ((*dest++ = *src++) != '\0')
		/* nothing */;
	return tmp;
}

/**
 * strlen - Find the length of a string
 * @s: The string to be sized
 */
qe_size qe_strlen(const char *s)
{
	const char *sc;

	for (sc = s; *sc != '\0'; ++sc)
		/* nothing */;
	return sc - s;
}

/**
 * strnlen - Find the length of a length-limited string
 * @s: The string to be sized
 * @count: The maximum number of bytes to search
 */
qe_size qe_strnlen(const char *s, qe_size count)
{
	const char *sc;

	for (sc = s; count-- && *sc != '\0'; ++sc)
		/* nothing */;
	return sc - s;
}
/**
 * strstr - Find the first substring in a %NUL terminated string
 * @s1: The string to be searched
 * @s2: The string to search for
 */
char *qe_strstr(const char *s1, const char *s2)
{
	int l1, l2;

	l2 = qe_strlen(s2);
	if (!l2)
		return (char *) s1;
	l1 = qe_strlen(s1);
	while (l1 >= l2) {
		l1--;
		if (!qe_memcmp(s1,s2,l2))
			return (char *) s1;
		s1++;
	}
	return QE_NULL;
}

/**
 * strrchr - Find the last occurrence of a character in a string
 * @s: The string to be searched
 * @c: The character to search for
 */
char *qe_strrchr(const char *s, int c)
{
    const char *p = s + qe_strlen(s);
    do {
    if (*p == (char)c)
        return (char *)p;
    } while (--p >= s);
    return QE_NULL;
}

/**
 * strcspn - Calculate the length of the initial substring of @s which does
 * not contain letters in @reject
 * @s: The string to be searched
 * @reject: The string to avoid
 */
qe_size qe_strcspn(const char *s, const char *reject)
{
	const char *p;
	const char *r;
	qe_size count = 0;

	for (p = s; *p != '\0'; ++p) {
		for (r = reject; *r != '\0'; ++r) {
			if (*p == *r)
				return count;
		}
		++count;
	}
	return count;
}

char *qe_strdup(const char *s)
{
	char *new;

	if ((s == QE_NULL)	||
	    ((new = qe_malloc(qe_strlen(s) + 1)) == QE_NULL) ) {
		return QE_NULL;
	}

	qe_strcpy(new, s);
	return new;
}

char *qe_strndup(const char *s, qe_size n)
{
	qe_size len;
	char *new;

	if (s == QE_NULL)
		return QE_NULL;

	len = qe_strlen(s);

	if (n < len)
		len = n;

	new = qe_malloc(len + 1);
	if (new == QE_NULL)
		return QE_NULL;

	qe_strncpy(new, s, len);
	new[len] = '\0';

	return new;
}

/**
 * strspn - Calculate the length of the initial substring of @s which only
 *	contain letters in @accept
 * @s: The string to be searched
 * @accept: The string to search for
 */
qe_size qe_strspn(const char *s, const char *accept)
{
	const char *p;
	const char *a;
	qe_size count = 0;

	for (p = s; *p != '\0'; ++p) {
		for (a = accept; *a != '\0'; ++a) {
			if (*p == *a)
				break;
		}
		if (*a == '\0')
			return count;
		++count;
	}

	return count;
}

/**
 * strpbrk - Find the first occurrence of a set of characters
 * @cs: The string to be searched
 * @ct: The characters to search for
 */
char *qe_strpbrk(const char *cs, const char *ct)
{
	const char *sc1, *sc2;

	for (sc1 = cs; *sc1 != '\0'; ++sc1) {
		for (sc2 = ct; *sc2 != '\0'; ++sc2) {
			if (*sc1 == *sc2)
				return (char *)sc1;
		}
	}
	return QE_NULL;
}

char * ___strtok;

/**
 * strtok - Split a string into tokens
 * @s: The string to be searched
 * @ct: The characters to search for
 *
 * WARNING: strtok is deprecated, use strsep instead.
 */
char *qe_strtok(char *s, const char *ct)
{
	char *sbegin, *send;

	sbegin  = s ? s : ___strtok;
	if (!sbegin) {
		return QE_NULL;
	}
	sbegin += qe_strspn(sbegin,ct);
	if (*sbegin == '\0') {
		___strtok = QE_NULL;
		return QE_NULL;
	}
	send = qe_strpbrk( sbegin, ct);
	if (send && *send != '\0')
		*send++ = '\0';
	___strtok = send;
	return (sbegin);
}

/**
 * strsep - Split a string into tokens
 * @s: The string to be searched
 * @ct: The characters to search for
 *
 * strsep() updates @s to point after the token, ready for the next call.
 *
 * It returns empty tokens, too, behaving exactly like the libc function
 * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
 * Same semantics, slimmer shape. ;)
 */
char *qe_strsep(char **s, const char *ct)
{
	char *sbegin = *s, *end;

	if (sbegin == QE_NULL)
		return QE_NULL;

	end = qe_strpbrk(sbegin, ct);
	if (end)
		*end++ = '\0';
	*s = end;

	return sbegin;
}

/**
 * strswab - swap adjacent even and odd bytes in %NUL-terminated string
 * s: address of the string
 *
 * returns the address of the swapped string or NULL on error. If
 * string length is odd, last byte is untouched.
 */
char *qe_strswab(const char *s)
{
	char *p, *q;

	if ((QE_NULL == s) || ('\0' == *s)) {
		return (QE_NULL);
	}

	for (p=(char *)s, q=p+1; (*p != '\0') && (*q != '\0'); p+=2, q+=2) {
		char  tmp;

		tmp = *p;
		*p  = *q;
		*q  = tmp;
	}

	return (char *) s;
}

/**
 * memchr - Find a character in an area of memory.
 * @s: The memory area
 * @c: The byte to search for
 * @n: The size of the area.
 *
 * returns the address of the first occurrence of @c, or %NULL
 * if @c is not found
 */
void *qe_memchr(const void *s, int c, qe_size n)
{
	const unsigned char *p = s;
	while (n-- != 0) {
		if ((unsigned char)c == *p++) {
			return (void *)(p-1);
		}
	}
	return QE_NULL;
}

#if (CONFIG_PRINTF_LONGLONG == 1)
static inline int divide(long long *n, int base)
{
    int res;

    /* optimized for processor which does not support divide instructions. */
    if (base == 10)
    {
        res = (int)(((unsigned long long)*n) % 10U);
        *n = (long long)(((unsigned long long)*n) / 10U);
    }
    else
    {
        res = (int)(((unsigned long long)*n) % 16U);
        *n = (long long)(((unsigned long long)*n) / 16U);
    }

    return res;
}
#else
static inline int divide(long *n, int base)
{
    int res;

    /* optimized for processor which does not support divide instructions. */
    if (base == 10)
    {
        res = (int)(((unsigned long)*n) % 10U);
        *n = (long)(((unsigned long)*n) / 10U);
    }
    else
    {
        res = (int)(((unsigned long)*n) % 16U);
        *n = (long)(((unsigned long)*n) / 16U);
    }

    return res;
}
#endif

static inline int skip_atoi(const char **s)
{
    register int i = 0;
    while (_ISDIGIT(**s))
        i = i * 10 + *((*s)++) - '0';

    return i;
}

#define ZEROPAD     (1 << 0)    /* pad with zero */
#define SIGN        (1 << 1)    /* unsigned/signed long */
#define PLUS        (1 << 2)    /* show plus */
#define SPACE       (1 << 3)    /* space if plus */
#define LEFT        (1 << 4)    /* left justified */
#define SPECIAL     (1 << 5)    /* 0x */
#define LARGE       (1 << 6)    /* use 'ABCDEF' instead of 'abcdef' */

#if (CONFIG_PRINTF_PRECISION == 1)
static char *sprint_number(char *buf,
                          char *end,
#if (CONFIG_PRINTF_LONGLONG == 1)
                          long long  num,
#else
                          long  num,
#endif
                          int   base,
                          int   s,
                          int   precision,
                          int   type)
#else
static char *sprint_number(char *buf,
                          char *end,
#if (CONFIG_PRINTF_LONGLONG == 1)
                          long long  num,
#else
                          long  num,
#endif
                          int   base,
                          int   s,
                          int   type)
#endif
{
    char c, sign;
#if (CONFIG_PRINTF_LONGLONG == 1)
    char tmp[32];
#else
    char tmp[16];
#endif
#if (CONFIG_PRINTF_PRECISION == 1)
    int precision_bak = precision;
#endif
    const char *digits;
    static const char small_digits[] = "0123456789abcdef";
    static const char large_digits[] = "0123456789ABCDEF";
    register int i;
    register int size;
    
    size = s;

    digits = (type & LARGE) ? large_digits : small_digits;
    if (type & LEFT)
        type &= ~ZEROPAD;

    c = (type & ZEROPAD) ? '0' : ' ';

    /* get sign */
    sign = 0;
    if (type & SIGN)
    {
        if (num < 0)
        {
            sign = '-';
            num = -num;
        }
        else if (type & PLUS)
            sign = '+';
        else if (type & SPACE)
            sign = ' ';
    }

#if (CONFIG_PRINTF_SPECIAL == 1)
    if (type & SPECIAL)
    {
        if (base == 16)
            size -= 2;
        else if (base == 8)
            size--;
    }
#endif

    i = 0;
    if (num == 0)
        tmp[i++] = '0';
    else
    {
        while (num != 0)
            tmp[i++] = digits[divide(&num, base)];
    }

#if (CONFIG_PRINTF_PRECISION == 1)
    if (i > precision)
        precision = i;
    size -= precision;
#else
    size -= i;
#endif

    if (!(type & (ZEROPAD | LEFT)))
    {
        if ((sign) && (size > 0))
            size--;

        while (size-- > 0)
        {
            if (buf < end)
                *buf = ' ';
            ++ buf;
        }
    }

    if (sign)
    {
        if (buf < end)
        {
            *buf = sign;
        }
        -- size;
        ++ buf;
    }

#if (CONFIG_PRINTF_SPECIAL == 1)
    if (type & SPECIAL)
    {
        if (base == 8)
        {
            if (buf < end)
                *buf = '0';
            ++ buf;
        }
        else if (base == 16)
        {
            if (buf < end)
                *buf = '0';
            ++ buf;
            if (buf < end)
            {
                *buf = type & LARGE ? 'X' : 'x';
            }
            ++ buf;
        }
    }
#endif

    /* no align to the left */
    if (!(type & LEFT))
    {
        while (size-- > 0)
        {
            if (buf < end)
                *buf = c;
            ++ buf;
        }
    }

#if (CONFIG_PRINTF_PRECISION == 1)
    while (i < precision--)
    {
        if (buf < end)
            *buf = '0';
        ++ buf;
    }
#endif

    /* put number in the temporary buffer */
#if (CONFIG_PRINTF_PRECISION == 1)
    while (i-- > 0 && (precision_bak != 0))
#else
    while (i-- > 0)
#endif
    {
        if (buf < end)
            *buf = tmp[i];
        ++ buf;
    }

    while (size-- > 0)
    {
        if (buf < end)
            *buf = ' ';
        ++ buf;
    }

    return buf;
}

#if (CONFIG_PRINTF_PRECISION == 1)
static char *sprint_float(char *buf,
                          char *end,
                          double val,
                          int   s,
                          int   precision,
                          int   type)
#else
static char *sprint_float(char *buf,
                          char *end,
                          double val,
                          int   s,
                          int   type)
#endif
{
    char *start = buf;

#if (CONFIG_PRINTF_LONGLONG == 1)
    long long integer;
#else
    long integer;
#endif
    double fraction;

#if (CONFIG_PRINTF_LONGLONG == 1)
    integer = (long long)val;
#else
    integer = (long)val;
#endif
    fraction = val - (double)integer;
    if (fraction < 0)
        fraction *= -1;

    /* print integer */
#if (CONFIG_PRINTF_PRECISION == 1)
    buf = sprint_number(buf, end, integer, 10, -1, -1, type);
#else
    buf = sprint_number(buf, end, integer, 10, -1, type);
#endif

    if (s > 0) {
        s -= (buf - start);
        if (s < 0)
            return buf;
    }

    if (buf < end)
        *buf = '.';
    buf++;

    /* print fraction */
#if (CONFIG_PRINTF_PRECISION == 1)
    if (precision <= 0)
        fraction *= qe_pow(10, 6);
    else
        fraction *= qe_pow(10, precision);
#else
    fraction *= qe_pow(10, 6);
#endif
    fraction += 0.5;

#if (CONFIG_PRINTF_LONGLONG == 1)
    integer = (long long)fraction;
#else
    integer = (long)fraction;
#endif

    /* print integer */
#if (CONFIG_PRINTF_PRECISION == 1)
    buf = sprint_number(buf, end, integer, 10, s, precision, type);
#else
    buf = sprint_number(buf, end, integer, 10, s, type);
#endif

    return buf;
}

void qe_weak qe_putc(char c)
{
}

#if (CONFIG_PRINTF_PRECISION == 1)
static int print_number(
#if (CONFIG_PRINTF_LONGLONG == 1)
    long long num,
#else
    long num,
#endif
    int base,
    int s, 
    int precision, 
    int type)
#else
static int print_number(
#if (CONFIG_PRINTF_LONGLONG == 1)
    long long num,
#else
    long num,
#endif
    int base,
    int s,
    int type)
#endif
{
    int nputs = 0;
    char c, sign;
    char tmp[32];
#if (CONFIG_PRINTF_PRECISION == 1)
    int precision_bak = precision;
#endif
    const char *digits;
    static const char small_digits[] = "0123456789abcdef";
    static const char large_digits[] = "0123456789ABCDEF";
    register int i;
    register int size;
    
    size = s;

    digits = (type & LARGE) ? large_digits : small_digits;
    if (type & LEFT)
        type &= ~ZEROPAD;

    c = (type & ZEROPAD) ? '0' : ' ';

    /* get sign */
    sign = 0;
    if (type & SIGN) {
        if (num < 0) {
            sign = '-';
            num = -num;
        } else if (type & PLUS)
            sign = '+';
        else if (type & SPACE)
            sign = ' ';
    }

#if (CONFIG_PRINTF_SPECIAL == 1)
    if (type & SPECIAL) {
        if (base == 16)
            size -= 2;
        else if (base == 8)
            size--;
    }
#endif

    i = 0;
    if (num == 0)
        tmp[i++] = '0';
    else {
        while (num != 0)
            tmp[i++] = digits[divide(&num, base)];
    }

#if (CONFIG_PRINTF_PRECISION == 1)
    if (i > precision)
        precision = i;
    size -= precision;
#endif

    if (!(type & (ZEROPAD | LEFT))) {
        if ((sign) && (size > 0))
            size--;

        while (size-- > 0) {
            qe_putc(' ');
            nputs++;
        }
    }

    if (sign) {
        -- size;
        qe_putc(sign);
        nputs++;
    }

#if (CONFIG_PRINTF_SPECIAL == 1)
    if (type & SPECIAL)
    {
        if (base == 8) {
            qe_putc('0');
            nputs++;
        } else if (base == 16) {
            qe_putc('0');
            qe_putc(type & LARGE ? 'X' : 'x');
            nputs += 2;
        }
    }
#endif

    /* no align to the left */
    if (!(type & LEFT)) {
        while (size-- > 0) {
            qe_putc(c);
            nputs++;
        }
    }

#if (CONFIG_PRINTF_PRECISION == 1)
    while (i < precision--) {
        qe_putc('0');
        nputs++;
    }
#endif

    /* put number in the temporary buffer */
#if (CONFIG_PRINTF_PRECISION == 1)
    while (i-- > 0 && (precision_bak != 0)) {
#else
    while (i-- > 0) {
#endif
        qe_putc(tmp[i]);
        nputs++;
    }

    while (size-- > 0) {
        qe_putc(' ');
        nputs++;
    }

    return nputs;
}

#if (CONFIG_PRINTF_PRECISION == 1)
static int print_float(double val, int s, int precision, int type)
#else
static int print_float(double val, int s, int type)
#endif
{
    int n;
    int nputs = 0;
#if (CONFIG_PRINTF_LONGLONG == 1)
    long long integer;
#else
    long integer;
#endif
    double fraction;

#if (CONFIG_PRINTF_LONGLONG == 1)
    integer = (long long)val;
#else
    integer = (long)val;
#endif
    fraction = val - (double)integer;
    if (fraction < 0)
        fraction = fraction * -1;

#if (CONFIG_PRINTF_PRECISION == 1)
    n = print_number(integer, 10, -1, -1, type);
#else
    n = print_number(integer, 10, -1, type);
#endif
    nputs += n;

    if (s > 0) {
        s -= n;
        if (s < 0) {
            return nputs;
        }
    }

    qe_putc('.');
    nputs++;

#if (CONFIG_PRINTF_PRECISION == 1)
    if (precision <= 0) 
        fraction *= qe_pow(10, 6);
    else
        fraction *= qe_pow(10, precision);
#else
    fraction *= qe_pow(10, 6);
#endif
    fraction += 0.5;

#if (CONFIG_PRINTF_LONGLONG == 1)
    integer = (long long)fraction;
#else
    integer = (long)fraction;
#endif

#if (CONFIG_PRINTF_PRECISION == 1)
    nputs += print_number(integer, 10, s, precision, type);
#else
    nputs += print_number(integer, 10, s, type);
#endif
    return nputs;
}

int qe_printf(const char *fmt, ...)
{
    int i, len, nputs=0;
#if (CONFIG_PRINTF_LONGLONG == 1)
    unsigned long long num;
#else
    qe_u32 num;
#endif
    char c;
    const char *s;

    qe_u8 base;             /* the base of number, 2/8/10/16 */
    qe_u16 flags;           /* flags to print number */
    qe_u8 qualifier;        /* 'h', 'l', or 'L' for integer fields */
    qe_s32 field_width;     /* width of output field */

    va_list args;

#if (CONFIG_PRINTF_PRECISION == 1)
    int precision;          /* min. # of digits for integers and max for a string */
#endif

    va_start(args, fmt);

    for (; *fmt ; ++fmt) {
        
        if (*fmt != '%') {
            qe_putc(*fmt);
            nputs++;
            continue;
        }

        /* process flags */
        flags = 0;

        while (1) {
            /* skips the first '%' also */
            ++ fmt;
            if (*fmt == '-') flags |= LEFT;
            else if (*fmt == '+') flags |= PLUS;
            else if (*fmt == ' ') flags |= SPACE;
            else if (*fmt == '#') flags |= SPECIAL;
            else if (*fmt == '0') flags |= ZEROPAD;
            else break;
        }

        /* get field width */
        field_width = -1;
        if (_ISDIGIT(*fmt)) {
            field_width = skip_atoi(&fmt);
        } else if (*fmt == '*') {
            ++fmt;
            /* it's the next argument */
            field_width = va_arg(args, int);
            if (field_width < 0) {
                field_width = -field_width;
                flags |= LEFT;
            }
        }

#if (CONFIG_PRINTF_PRECISION == 1)
        /* get the precision */
        precision = -1;
        if (*fmt == '.') {
            ++ fmt;
            if (_ISDIGIT(*fmt)) {
                precision = skip_atoi(&fmt);
            } else if (*fmt == '*') {
                ++ fmt;
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0) precision = 0;
        }
#endif

        /* get the conversion qualifier */
        qualifier = 0;
#if (CONFIG_PRINTF_LONGLONG == 1)
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
#else
        if (*fmt == 'h' || *fmt == 'l')
#endif
        {
            qualifier = *fmt;
            ++ fmt;
#if (CONFIG_PRINTF_LONGLONG == 1)
            if (qualifier == 'l' && *fmt == 'l')
            {
                qualifier = 'L';
                ++ fmt;
            }
#endif
        }

        /* the default base */
        base = 10;

        switch (*fmt) {

        case 'c':
            if (!(flags & LEFT)) {
                while (--field_width > 0) {
                    qe_putc(' ');
                    nputs++;
                }
            }
            /* get character */
            c = (char)va_arg(args, int);
            qe_putc(c);
            nputs++;
            /* put width */
            while (--field_width > 0) {
                qe_putc(' ');
                nputs++;
            }
            continue;

        case 's':
            s = va_arg(args, char *);
            if (!s) s = "(NULL)";
            len = qe_strlen(s);
#if (CONFIG_PRINTF_PRECISION == 1)
            if (precision > 0 && len > precision) len = precision;
#endif
            if (!(flags & LEFT)) {
                while (len < field_width--) {
                    qe_putc(' ');
                    nputs++;
                }
            }
            for (i = 0; i < len; ++i) {
                qe_putc(*s);
                nputs++;
                ++s;
            }
            while (len < field_width--) {
                qe_putc(' ');
                nputs++;
            }
            continue;

#if (CONFIG_PRINTF_FLOAT == 1)
        case 'f':
            flags |= SIGN;
#if (CONFIG_PRINTF_PRECISION == 1)
            nputs += print_float(
                va_arg(args, double), 
                field_width, precision, flags);
#else
            nputs += print_float(
                va_arg(args, double),
                field_width, flags);
#endif
            continue;
#endif

        case 'p':
            if (field_width == -1) {
                field_width = sizeof(void *) << 1;
                flags |= ZEROPAD;
            }
#if (CONFIG_PRINTF_PRECISION == 1)
            nputs += print_number(
                (qe_base)va_arg(args, void *), 
                16, field_width, precision, flags);
#else
            nputs += print_number(
            (qe_base)va_arg(args, void *), 
            16, field_width, flags);
#endif
            continue;

        case '%':
            qe_putc('%');
            nputs++;
            continue;

        /* integer number formats - set up the flags and "break" */
        case 'o':
            base = 8;
            break;

        case 'X':
            flags |= LARGE;
        case 'x':
            base = 16;
            break;

        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            break;

        default:
            qe_putc('%');
            nputs++;
            if (*fmt) {
                qe_putc(*fmt);
                nputs++;
            } else {
                -- fmt;
            }
            continue;
        }

#if (CONFIG_PRINTF_LONGLONG == 1)
        if (qualifier == 'L') num = va_arg(args, long long);
        else if (qualifier == 'l')
#else
        if (qualifier == 'l')
#endif 
        {
            num = va_arg(args, qe_u32);
            if (flags & SIGN) num = (qe_s32)num;
        } else if (qualifier == 'h') {
            num = (qe_u16)va_arg(args, qe_s32);
            if (flags & SIGN) num = (qe_s16)num;
        } else {
            num = va_arg(args, qe_u32);
            if (flags & SIGN) num = (qe_s32)num;
        }
#if (CONFIG_PRINTF_PRECISION == 1)
        nputs += print_number(num, base, field_width, precision, flags);
#else
        nputs += print_number(num, base, field_width, flags);
#endif
    }

    va_end(args);

    return nputs;
}

int qe_sprintf(char *buf, const char *fmt, ...)
{
    int n;
    va_list args;
    va_start(args, fmt);
    n = qe_vsprintf(buf, fmt, args);
    va_end(args);

    return n;
}

int qe_vsnprintf(char *buf, qe_size size, const char *fmt, va_list args)
{
#if (CONFIG_PRINTF_LONGLONG == 1)
    unsigned long long num;
#else
    qe_u32 num;
#endif
    int i, len;
    char *str, *end, c;
    const char *s;

    qe_u8 base;            /* the base of number */
    qe_u16 flags;           /* flags to print number */
    qe_u8 qualifier;       /* 'h', 'l', or 'L' for integer fields */
    qe_s32 field_width;     /* width of output field */

#if (CONFIG_PRINTF_PRECISION == 1)
    int precision;      /* min. # of digits for integers and max for a string */
#endif
    
    str = buf;
    end = buf + size;

    /* Make sure end is always >= buf */
    if (end < buf)
    {
        end  = ((char *) - 1);
        size = end - buf;
    }

    for (; *fmt ; ++fmt)
    {
        if (*fmt != '%')
        {
            if (str < end)
                *str = *fmt;
            ++ str;
            continue;
        }

        /* process flags */
        flags = 0;

        while (1)
        {
            /* skips the first '%' also */
            ++ fmt;
            if (*fmt == '-') flags |= LEFT;
            else if (*fmt == '+') flags |= PLUS;
            else if (*fmt == ' ') flags |= SPACE;
            else if (*fmt == '#') flags |= SPECIAL;
            else if (*fmt == '0') flags |= ZEROPAD;
            else break;
        }

        /* get field width */
        field_width = -1;
        if (_ISDIGIT(*fmt)) field_width = skip_atoi(&fmt);
        else if (*fmt == '*')
        {
            ++ fmt;
            /* it's the next argument */
            field_width = va_arg(args, int);
            if (field_width < 0)
            {
                field_width = -field_width;
                flags |= LEFT;
            }
        }

#if (CONFIG_PRINTF_PRECISION == 1)
        /* get the precision */
        precision = -1;
        if (*fmt == '.')
        {
            ++ fmt;
            if (_ISDIGIT(*fmt)) precision = skip_atoi(&fmt);
            else if (*fmt == '*')
            {
                ++ fmt;
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0) precision = 0;
        }
#endif
        /* get the conversion qualifier */
        qualifier = 0;
#if (CONFIG_PRINTF_LONGLONG == 1)
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
#else
        if (*fmt == 'h' || *fmt == 'l')
#endif
        {
            qualifier = *fmt;
            ++ fmt;
#if (CONFIG_PRINTF_LONGLONG == 1)
            if (qualifier == 'l' && *fmt == 'l')
            {
                qualifier = 'L';
                ++ fmt;
            }
#endif
        }

        /* the default base */
        base = 10;

        switch (*fmt)
        {
        case 'c':
            if (!(flags & LEFT))
            {
                while (--field_width > 0)
                {
                    if (str < end) *str = ' ';
                    ++ str;
                }
            }

            /* get character */
            c = (qe_u8)va_arg(args, int);
            if (str < end) *str = c;
            ++ str;

            /* put width */
            while (--field_width > 0)
            {
                if (str < end) *str = ' ';
                ++ str;
            }
            continue;

        case 's':
            s = va_arg(args, char *);
            if (!s) s = "(NULL)";

            len = qe_strlen(s);
#if (CONFIG_PRINTF_PRECISION == 1)
            if (precision > 0 && len > precision) len = precision;
#endif

            if (!(flags & LEFT))
            {
                while (len < field_width--)
                {
                    if (str < end) *str = ' ';
                    ++ str;
                }
            }

            for (i = 0; i < len; ++i)
            {
                if (str < end) *str = *s;
                ++ str;
                ++ s;
            }

            while (len < field_width--)
            {
                if (str < end) *str = ' ';
                ++ str;
            }
            continue;

#if (CONFIG_PRINTF_FLOAT == 1)
        case 'f':
        flags |= SIGN;
#if (CONFIG_PRINTF_PRECISION == 1)
            str = sprint_float(str, end,
                va_arg(args, double),
                field_width, precision, flags);
#else
            str = sprint_float(str, end,
                va_arg(args, double),
                field_width, flags);
#endif
            continue;
#endif

        case 'p':
            if (field_width == -1)
            {
                field_width = sizeof(void *) << 1;
                flags |= ZEROPAD;
            }
#if (CONFIG_PRINTF_PRECISION == 1)
            str = sprint_number(str, end,
                               (qe_base)va_arg(args, void *),
                               16, field_width, precision, flags);
#else
            str = sprint_number(str, end,
                               (long)va_arg(args, void *),
                               16, field_width, flags);
#endif
            continue;

        case '%':
            if (str < end) *str = '%';
            ++ str;
            continue;

        /* integer number formats - set up the flags and "break" */
        case 'o':
            base = 8;
            break;

        case 'X':
            flags |= LARGE;
        case 'x':
            base = 16;
            break;

        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            break;

        default:
            if (str < end) *str = '%';
            ++ str;

            if (*fmt)
            {
                if (str < end) *str = *fmt;
                ++ str;
            }
            else
            {
                -- fmt;
            }
            continue;
        }

#if (CONFIG_PRINTF_LONGLONG == 1)
        if (qualifier == 'L') num = va_arg(args, long long);
        else if (qualifier == 'l')
#else
        if (qualifier == 'l')
#endif
        {
            num = va_arg(args, qe_u32);
            if (flags & SIGN) num = (qe_s32)num;
        }
        else if (qualifier == 'h')
        {
            num = (qe_u16)va_arg(args, qe_s32);
            if (flags & SIGN) num = (qe_s16)num;
        }
        else
        {
            num = va_arg(args, qe_u32);
            if (flags & SIGN) num = (qe_s32)num;
        }
#if (CONFIG_PRINTF_PRECISION == 1)
        str = sprint_number(str, end, num, base, field_width, precision, flags);
#else
        str = sprint_number(str, end, num, base, field_width, flags);
#endif
    }

    if (size > 0)
    {
        if (str < end) *str = '\0';
        else
        {
            end[-1] = '\0';
        }
    }

    /* the trailing null byte doesn't count towards the total
    * ++str;
    */
    return str - buf;
}

/**
 * This function will fill a formatted string to buffer
 *
 * @buf  : the buffer to save formatted string
 * @size : the size of buffer
 * @fmt  : the format
 */
int qe_snprintf(char *buf, qe_size size, const char *fmt, ...)
{
    int n;
    va_list args;

    va_start(args, fmt);
    n = qe_vsnprintf(buf, size, fmt, args);
    va_end(args);

    return n;
}

/**
 * This function will fill a formatted string to buffer
 *
 * @buf     : the buffer to save formatted string
 * @arg_ptr : the arg_ptr
 * @format  : the format
 */
int qe_vsprintf(char *buf, const char *format, va_list arg_ptr)
{
    return qe_vsnprintf(buf, (qe_size) - 1, format, arg_ptr);
}

qe_double qe_strtof(const char *s, char **endptr)
{
    double ret = 0.0;
    double fraction = 0.0;
    int sign = 1;
    int divisor = 1;

    /* skip space */
    while (*s == ' ' || *s == '\t' || *s == '\n')
        s++;

    if (*s == '-') {
        sign = -1;
        s++;
    } else if (*s == '+') {
        s++;
    }

    while (*s >= '0' && *s <= '9') {
        ret = ret * 10.0 + (*s - '0');
        s++;
    }

    if (*s == '.') {
        s++;
        while (*s >= '0' && *s <= '9') {
            fraction = fraction * 10.0 + (*s - '0');
            divisor *= 10;
            s++;
        }
    }

    if (endptr != QE_NULL)
        *endptr = (char *)s;

    ret = ret + fraction / divisor;
    return ret * sign;
}

qe_s64 qe_strtol(const char *str, char **endptr, int base)
{
    const char *ptr = str;
    qe_s64 result = 0;
    int sign = 1;
    int overflow = 0;

    /* skip space */
    while (qe_isspace((unsigned char)*ptr)) {
        ptr++;
    }

    if (*ptr == '+') {
        ptr++;
    } else if (*ptr == '-') {
        sign = -1;
        ptr++;
    }

    if (base == 0) {
        if (*ptr == '0') {
            ptr++;
            if (qe_tolower((unsigned char)*ptr) == 'x') {
                base = 16;
                ptr++;
            } else {
                base = 8;
            }
        } else {
            base = 10;
        }
    } else if (base < 2 || base > 36) {
        if (endptr != QE_NULL) {
            *endptr = (char *)str;
        }
        return 0;
    }

    if (base == 16) {
        if (*ptr == '0' && qe_tolower((unsigned char)ptr[1]) == 'x') {
            ptr += 2;
        }
    }

    while (*ptr != '\0') {
        int digit;
        char c = *ptr;

        if (qe_isdigit((unsigned char)c)) {
            digit = c - '0';
        } else if (qe_isalpha((unsigned char)c)) {
            digit = qe_tolower((unsigned char)c) - 'a' + 10;
        } else {
            break;
        }

        if (digit >= base) {
            break;
        }

        if (result > (QE_S64_MAX - digit) / base) {
            overflow = 1;
        }

        result = result * base + digit;

        ptr++;
    }

    if (sign == -1) {
        result = -result;
    }

    if (overflow) {
        result = (sign == 1) ? QE_S64_MAX : QE_S64_MIN;
    }

    if (endptr != QE_NULL) {
        *endptr = (char *)(overflow ? str : ptr);
    }

    return result;
}
#endif

#if !defined(HAVE_VASPRINTF) || defined(CONFIG_NANO)
int qe_vasprintf(char **strp, const char *fmt, va_list ap)
{
    va_list ap_copy;
    va_copy(ap_copy, ap);
    int length = qe_vsnprintf(QE_NULL, 0, fmt, ap_copy);
    va_end(ap_copy);

    if (length < 0) {
        return -1;
    }

    *strp = qe_malloc(length + 1);
    if (*strp == QE_NULL) {
        return -1;
    }

    qe_vsnprintf(*strp, length + 1, fmt, ap);

    return length;
}
#endif

qe_int qe_strtoi(qe_const_str s)
{
    qe_int result = 0;
    int base = 0;
    int sign = 1;
    int digit;

    /* skip space */
    while (*s == ' ' || *s == '\t' || *s == '\n')
        s++;

    /* process sign */
    if (*s == '-') {
        sign = -1;
        s++;
    } else if (*s == '+') {
        s++;
    }

    /* process base */
    if (qe_strlen(s) > 1 && *s == '0') {
        if (s[1] == 'x' || s[1] == 'X') {
            base = 16;
            s += 2;
        } else if (s[1] == 'b' || s[1] == 'B') {
            base = 2;
            s += 2;
        } else {
            base = 8;
            s += 2;
        }
    } else {
        base = 10;
    }

    while (*s) {
        if (qe_isdigit(*s)) {
            digit = *s - '0';
        } else if (qe_isalpha(*s)) {
            digit = qe_tolower(*s) - 'a' + 10;
        } else {
            break;
        }
        if (digit >= base)
            break;

        result = result * base + digit;
        s++;
    }
    return sign * result;
}

qe_ubase qe_hexstr_to_u32(qe_const_str str)
{
    int i;
    qe_u8 val;
    qe_ubase result;
    const char *p = str;

    if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
        p += 2;
    }

    for (i=0; i<8 && p[i] != '\0'; i++) {
        char c = p[i];
        c = qe_toupper(c);
        if (c >= '0' && c <= '9') {
            val = c - '0';
        } else if (c >= 'A' && c <= 'F') {
            val = c - 'A' + 10;
        } else {
            break;
        }
        result = (result << 4) | val;
    }
    return result;
}

char *qe_strdup_format_args(const char *format, va_list args)
{
    char *string = QE_NULL;

    qe_vasprintf(&string, format, args);

    return string;
}

char *qe_strdup_format(const char *format, ...)
{
    char *string;
    va_list args;
    va_start(args, format);
    string = qe_strdup_format_args(format, args);
    va_end(args);
    return string;
}

static qe_size nearest_pow(qe_size num)
{
    qe_size n = num - 1;

    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;

    return n + 1;
}

static void qe_string_expand(qe_string *string, qe_size len)
{
    string->allocated_len = nearest_pow(string->len + len + 1);
    if (string->allocated_len == 0)
        string->allocated_len = string->len + len + 1;
    string->str = qe_realloc(string->str, string->allocated_len);
    qe_assert(string->str != QE_NULL);
}

static void qe_string_maybe_expand(qe_string *string,
    qe_size len)
{
    if (string->len + len >= string->allocated_len)
        qe_string_expand(string, len);
}

/**
 * @brief Cuts off the end of the string, leaving the first @len bytes.
 * @string: a string
 * @len: the new size of string
 *
 * @return string
 */
qe_string *qe_string_truncate(qe_string *string, qe_size len)
{
    if (!string)
        return QE_NULL;

    string->len = qe_min(len, string->len);
    string->str[string->len] = 0;

    return string;
}

qe_string *qe_string_insert_len(qe_string *string, qe_base pos, 
    const char *val, qe_base len)
{
    qe_size len_unsigned, pos_unsigned;

    if (!string || !val) {
        return QE_NULL;
    }
    
    if (len == 0)
        return string;

    if (len < 0)
        len = qe_strlen(val);
    len_unsigned = len;

    if (pos < 0) {
        pos_unsigned = string->len;
    } else {
        pos_unsigned = pos;
        if (pos_unsigned > string->len)
            return QE_NULL;
    }

    /* Check whether val represents a substring of string. */
    if (qe_unlikely(val >= string->str && val <= string->str + string->len)) {
        qe_size offset = val - string->str;
        qe_size precount = 0;

        qe_string_maybe_expand(string, len_unsigned);
        val = string->str + offset;
        /* At this point, val is valid again. */

        /* Open up space where we are going to insert. */
        if (pos_unsigned < string->len)
            qe_memmove(string->str + pos_unsigned + len_unsigned,
                string->str + pos_unsigned, string->len - pos_unsigned);
    
        /* Move the source part before the gap, if any. */
        if (offset < pos_unsigned) {
            precount = qe_min(len_unsigned, pos_unsigned - offset);
            qe_memcpy(string->str + pos_unsigned, (void *)val, precount);
        }
    
        /* Move the source part after the gap, if any. */
        if (len_unsigned > precount)
            qe_memcpy(string->str + pos_unsigned + precount,
                (void *)(val + precount + len_unsigned), len_unsigned - precount);
    } else {

        qe_string_maybe_expand(string, len_unsigned);
        
        /* 
         * If we aren't appending at the end, move a hunk
         * of the old string to the end, opening up space
         */
        if (pos_unsigned < string->len)
            qe_memmove(string->str + pos_unsigned + len_unsigned,
                 string->str + pos_unsigned, string->len - pos_unsigned);

        /* insert the new string */
        if (len_unsigned == 1)
            string->str[len_unsigned] = *val;
        else
            qe_memcpy(string->str + pos_unsigned, (void *)val, len_unsigned);
    }

    string->len += len_unsigned;
    string->str[string->len] = 0;

    return string;
}

/**
 * Insert to a string
 * 
 * @param[in] string : the string
 * @param[in] pos    : the position to insert the copy of the string
 * @param[in] val    : the string to insert
 *
 * @retuurn string
 */
qe_string *qe_string_insert(qe_string *string, qe_offs pos,
    const char *val)
{
    return qe_string_insert_len(string, pos, val, -1);
}

/**
 * @brief Inserts a char into a string, expanding it if necessary.
 * 
 * @param[in] string: string
 * @param[in] pos: the position to insert the byte
 * @param[in] c: the byte to insert
 *
 * @return: (transfer none): string
 */
qe_string *qe_string_insert_char(qe_string *string,
    qe_offs pos, char c)
{
    qe_size pos_unsigned;

    if (!string)
        return QE_NULL;

    qe_string_maybe_expand(string, 1);

    if (pos < 0) {
        pos = string->len;
    } else {
        if (pos > string->len)
            return string;
    }

    pos_unsigned = pos;

    /* If not just an append, move the old stuff */
    if (pos_unsigned < string->len)
        qe_memmove(string->str + pos_unsigned + 1,
             string->str + pos_unsigned, 
             string->len - pos_unsigned);

    string->str[pos_unsigned] = c;

    string->len += 1;

    string->str[string->len] = 0;

    return string;
}

qe_string *qe_string_append_len(qe_string *string, 
    const char *val, qe_size len)
{
    return qe_string_insert_len(string, -1, val, len);
}

/**
 * @brief Append string
 * @param[in] string : string
 * @param[in] val    : the string to append onto the end of string
 *
 * @return string
 */
qe_string *qe_string_append(qe_string *string, const char *val)
{
    return qe_string_insert_len(string, -1, val, -1);
}

/**
 * @brief Create a new string with enough space
 * 
 * @param[in] size : the default size of space to hold string
 *
 */
qe_string *qe_string_new_size(qe_size size)
{
    qe_string *string = qe_malloc(size);
    qe_assert(string != QE_NULL);
    string->allocated_len = 0;
    string->len = 0;
    string->str = QE_NULL;
    qe_string_expand(string, qe_max(size, 64));
    string->str[0] = 0;
    return string;
}

/**
 * @brief Create a new string with initialized text
 * 
 * @param[in] init : init text
 * 
 * @note
 * This function will create a string with param <init>, 
 * if <init> is null, will create a empty string, else, copy
 * <init> test into string.
 */
qe_string *qe_string_new(const char *init)
{
    qe_string *string;

    if (init == QE_NULL || *init == '\0') {
        string = qe_string_new_size(2);
    } else {
        qe_size len;
        len = qe_strlen(init);
        string = qe_string_new_size(len + 2);
        qe_string_append_len(string, init, len);
    }

    return string;
}

qe_string *qe_string_new_len(const char *init, qe_size len)
{
    qe_string *string;

    if (len < 0) {
        return qe_string_new(init);
    } else {
        string = qe_string_new_size(len);
        if (init)
            qe_string_append_len(string, init, len);
        return string;
    }
}

void qe_string_append_vprintf(qe_string *string, const char *format, va_list args)
{
    int len;
    char *buf;

    if (!string)
        return;
    
    if (!format)
        return;

    len = qe_vasprintf(&buf, format, args);

    if (len >= 0) {
        qe_string_maybe_expand(string, len);
        qe_memcpy(string->str + string->len, buf, len + 1);
        string->len += len;
        qe_free(buf);
    }
}


void qe_string_format(qe_string *string, const char *format, ...)
{
    va_list args;

    qe_string_truncate(string, 0);

    va_start(args, format);
    qe_string_append_vprintf(string, format, args);
    va_end(args);
}

/**
 * @brief Append string with format
 * 
 * @param[in] string : the string
 * @param[in] format : the format
 * 
 * @note 
 * This function will append format vars into string.
 */
void qe_string_append_format(qe_string *string, const char *format, ...)
{
    va_list args;

    va_start(args, format);
    qe_string_append_vprintf(string, format, args);
    va_end(args);
}

/**
 * @brief Adds a string on to the start of a string
 * expanding it if necessary.
 * 
 * @param[in] string: a string
 * @param[in] val: the string to prepend on the start of string
 *
 * @return string
 */
qe_string *qe_string_prepend(qe_string *string, const char *val)
{
    return qe_string_insert_len(string, 0, val, -1);
}

/**
 * @brief Adds a char onto the start of a string, expanding it if necessary.
 * 
 * @param[in] string: a string
 * @param[in] c: the byte to prepend on the start of the string
 *
 * @return string
 */
qe_string *qe_string_prepend_char(qe_string *string, char c)
{
    if (!string)
        return QE_NULL;

  return qe_string_insert_char(string, 0, c);
}

/**
 * @brief Prepends @len bytes of @val to @string.
 * 
 * @string: string
 * @val: bytes to prepend
 * @len: number of bytes in @val to prepend, or -1 for all of @val
 *
 * @note
 * If @len is positive, @val may contain embedded nuls and need
 * not be nul-terminated. It is the caller's responsibility to
 * ensure that @val has at least @len addressable bytes.
 *
 * If @len is negative, @val must be nul-terminated and @len
 * is considered to request the entire string length. This
 * makes g_string_prepend_len() equivalent to g_string_prepend().
 *
 * @return string
 */
qe_string *qe_string_prepend_len(qe_string *string, const char *val,
    qe_offs len)
{
    return qe_string_insert_len(string, 0, val, len);
}

/**
 * @brief Free a string
 * 
 * @param[in] string : string object
 * @param[in] free_segment : if TRUE, the actual character data is freed as well
 */
char *qe_string_free(qe_string *string, qe_bool free_segment)
{
    char *segment;

    if (!string)
        return QE_NULL;

    if (free_segment) {
        qe_free(string->str);
        segment = QE_NULL;
    } else {
        segment = string->str;
    }

    qe_free(string);

    return segment;
}

char *qe_string_free_and_steal(qe_string *string)
{
    return qe_string_free(string, qe_false);
}

qe_string *qe_string_append_char(qe_string *string, char c)
{
    if (!string)
        return QE_NULL;

    return qe_string_insert_char(string, -1, c);
}

qe_bool qe_string_equal(const qe_string *s1, const qe_string *s2)
{
    char *p, *q;
    qe_string *string1 = (qe_string *)s1;
    qe_string *string2 = (qe_string *)s2;
    qe_size i = string1->len;

    if (i != string2->len)
        return qe_false;

    p = string1->str;
    q = string2->str;

    while (i) {
        if (*p != *q)
            return qe_false;
        p++;
        q++;
        i--;
    }

    return qe_true;
}


/**
 * @brief Creates a hash code for string
 * 
 * @param[in] str: a string to hash
 * 
 * Returns: hash code for @str
 */
qe_u32 qe_string_hash(const qe_string *str)
{
    const char *p = str->str;
    qe_size n = str->len;
    qe_u32 h = 0;

    /* 31 bit hash function */
    while (n--) {
        h = (h << 5) - h + *p;
        p++;
    }

    return h;
}

/**
 * @brief Erase string at position
 * 
 * @param[in] string: the string
 * @param[in] pos   : erase position
 * @param[in] len   : erase length
 * 
 * @return string
 */
qe_string *qe_string_erase(qe_string *string, qe_offs pos, int len)
{
    qe_uint len_unsigned, pos_unsigned;

    if (!string)
        return QE_NULL;
    
    if (pos < 0)
        return string;

    pos_unsigned = pos;

    if (pos_unsigned > string->len)
        return QE_NULL;

    if (len < 0) {
        len_unsigned = string->len - pos_unsigned;
    } else {

        len_unsigned = len;
        if (pos_unsigned + len_unsigned > string->len)
            return string;
        
        if (pos_unsigned + len_unsigned < string->len)
            qe_memmove(string->str + pos_unsigned, 
                       string->str + pos_unsigned + len_unsigned,
                       string->len - (pos_unsigned + len_unsigned));
    }

    string->len -= len_unsigned;

    string->str[string->len] = 0;

    return string;
}


qe_uint qe_string_replace(qe_string *string, const char *find, const char *replace, 
    qe_uint limit)
{
    qe_size f_len, r_len, pos;
    char *curr, *next;
    qe_uint n = 0;

    if (!string || !find || !replace)
        return 0;

    f_len = qe_strlen (find);
    r_len = qe_strlen (replace);
    curr  = string->str;

    while ((next = qe_strstr(curr, find)) != QE_NULL) {
        pos = next - string->str;
        qe_string_erase(string, pos, f_len);
        qe_string_insert(string, pos, replace);
        curr = string->str + pos + r_len;
        n++;
        /* Only match the empty string once at any given position, to
         * avoid infinite loops */
        if (r_len == 0) {
            if (curr[0] == '\0')
                break;
            else
                curr++;
        }

        if (limit == n)
            break;
    }

    return n;
}

inline int qe_strb_args(qe_strb *builder, const char *fmt, ...)
{
	int len;
	va_list ap;
	
	va_start(ap, fmt);
	len = qe_vsnprintf(builder->p, builder->max, fmt, ap);
	va_end(ap);

	builder->max -= len;
	builder->len += len;
	builder->p += len;
	
	return len;
}

inline qe_strb qe_strb_frombuf(char *buf, int size)
{
	qe_strb builder;

	builder.head = buf;
	builder.len  = 0;
	builder.max  = size;
	builder.p    = buf;
	return builder;
}

inline qe_strb qe_str_builder_make(char *s, int max)
{
	qe_strb builder;

	builder.p = s;
	builder.head = s;
	builder.max = max;
	builder.len = qe_strlen(s);
	return builder;
}

qe_const_str qe_byte_to_str(qe_uint bytes)
{
    static char str[16] = {'\0'};

    if (bytes >= QE_SIZE_GB) {
        qe_sprintf(str, "%.02f(GB)", (qe_double)bytes / QE_SIZE_GB);
    } else if (bytes >= QE_SIZE_MB) {
        qe_sprintf(str, "%.02f(MB)", (qe_double)bytes / QE_SIZE_MB);
    } else if (bytes >= QE_SIZE_KB) {
        qe_sprintf(str, "%.02f(KB)", (qe_double)bytes / QE_SIZE_KB);
    } else {
        qe_sprintf(str, "%u(B)", bytes);
    }

    return str;
}

void qe_hexdump(qe_const_ptr vbuf, qe_uint len)
{
    qe_uint n;
	qe_u8 *buf = (qe_u8 *)vbuf;

    for (n = 0; n < len;) {
		qe_uint start = n, m;
		char line[128], *p = line;

		p += qe_snprintf(p, 10, "%04X: ", start);
		for (m = 0; m < 16 && n < len; m++)
			p += qe_snprintf(p, 5, "%02X ", buf[n++]);

		while (m++ < 16)
			p += qe_snprintf(p, 5, "   ");

		p += qe_snprintf(p, 6, "   ");
		for (m = 0; m < 16 && (start + m) < len; m++) {
			if (buf[start + m] >= ' ' && buf[start + m] < 127)
				*p++ = buf[start + m];
			else
				*p++ = '.';
		}

		while (m++ < 16)
			*p++ = ' ';

		*p = '\0';
		qe_printf("%s"QE_ENDLINE, line);
	}
}